Fork me on GitHub

SPRING AOP

注意:所有文章除特别说明外,转载请注明出处.

第3章 Spring AOP的实现

[toc]

3.1 AOP概念

3.1.2 Advice通知

Advice(通知)定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中主要描述Spring AOP围绕方法调用而注入的切面行为。

在BeforeAdvice的继承关系中,定义了为待增强的目标方法设置的前置增强接口MethodBeforeAdvice,使用这个前置接口需要实现一个回调函数:

void before(Method method, Object[] args, Object target) throws Throwable;

在AfterReturning-Advice接口中定义了接口方法:

void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

在对于ThrowsAdvice,并没有指定需要实现的接口方法,它在抛出异常时被回调,这个回调是AOP使用反射机制来完成的。

3.1.3 PointCut切点

切点决定Advice通知应该作用于哪个连接点,即通过pointcut来定义需要增强方法的集合,这些集合可以选择按照一定的规则来完成。

在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher。对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者是否需要对当前调用方法应用配置好的Advice通知。

在JdkRegexpMethodPointcut的基类 StaticMethodMatcherPointcut的实现中可以看到,设置MethodMatcher为StaticMethodMatcher,同时JdkRegexpMethodPointcut也是这个MethodMatcher的子类。

在Pointcut的类继承关系中,MethodMatcher对象实际上是可以被配置成JdkRegexpMethodPointcut来完成方法的匹配判断的。在JdkRegexpMethodPointcut中,可以看到一个matches方法,这个matches方法是MethodMatcher定义的接口方法。在JdkRegexpMethodPointcut的实现中,这个matches方法就是使用正则表达式来对方法名进行匹配的地方。

在JdkDynamicAopProxy的invoke方法中触发了对matches方法的调用。

3.1.4 Advisor通知器

将切面切面增强设计(Advice)与切点(pointcut)结合起来的方法就是Advisor。通过Advisor可以定义应该使用哪个通知并在哪个关注点使用它。

在DefaultPointcutAdvisor中,有两个属性,分别是advice和pointcut。通过这两个属性,可以分别配置Advice和Pointcut。

3.2 Spring AOP 的设计与实现

3.2.1 JVM动态代理实现

在Spring AOP实现中,使用的核心技术是动态代理,而这种动态代理实际上是JDK的一个特性(在JDK 1.3以上的版本里,实现了动态代理模式)。通过JDK的动态代理特性,可以为任意Java对象创建代理对象,对于具体使用来说,这个特性是通过Java Reflection API来完成的。

JDK中已经实现了Proxy模式,在基于Java虚拟机设计应用程序时,只需要直接使用这个特性就可以了。具体来说,可以在Java的reflection包中看到Proxy对象,这个对象生成后,所起的作用就类似于Proxy模式中的Proxy对象。在使用时,还需要为代理对象(Proxy)设计一个回调方法,这个回调方法起到的作用是,在其中加入了作为代理需要额外处理的动作。在JDK中实现,需要实现下面所示的InvocationHandler接口:

public interface InvocationHandler {

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable;

}

invoke方法的第一个参数是代理对象实例,第二个参数是Method方法对象,代表的是当前Proxy被调用的方法,最后一个参数是被调用的方法中的参数。

Proxy类中invoke()方法在反射的基础上加载类里面的方法。Proxy.newInstance()方法生成具体Proxy对象时将InvocationHandler设置到参数里面便可。

3.2.2 Spring AOP设计分析

Spring AOP的核心技术是JDK动态代理技术。以动态代理技术为基础,设计出了一系列AOP的横切实现,比如前置通知、返回通知、异常通知等。同时,Spring AOP还提供了一系列的Pointcut来匹配切入点,可以使用现有的切入点来设计横切面,也可以扩展相关的Pointcut方法来实现切入需求。

在Spring AOP中,虽然对于AOP的使用者来说,只需要配置相关的Bean定义即可,但仔细分析Spring AOP的内部设计可以看到,为了让AOP起作用,需要完成一系列过程,比如,需要为目标对象建立代理对象,这个代理对象可以通过使用JDK的Proxy来完成,也可以通过第三方的类生成器cglib来完成。然后,还需要启动代理对象的拦截器来完成各种横切面的织入,这一系列的织入设计是通过一系列Adapter来实现的。通过一系列Adapter的设计,可以把AOP的横切面设计和Proxy模式有机地结合起来,从而实现在AOP中定义好的各种织入方式。

3.3 建立AopProxy代理对象

3.3.1 设计原理

在Spring AOP中,一个重要的部分是代理对象的生成,对于Spring应用,可以看到,是通过配置和调用Spring的ProxyFactoryBean来完成此任务的。在ProxyFactoryBean中,封装了主要代理对象的生成过程。在这个生成过程中,可以使用JDK的Proxy和CGLIB两种方式。

继承关系

在这个类继承关系中,AspectPxoxyFactory、ProxyFactory和ProxyFactoryBean,都在同一个类的继承体系下,都是ProxyConfig、AdvicedSupport 和ProxyCreatorSupport的子类。

共同基类ProxyConfig为ProxyFactoryBean这样的子类提供了配置属性。

基类AdvisedSupport的实现中,封装了AOP对通知后台通知器的相关操作,这些操作对于不同的AOP的代理对象的生成都是一样的,但对于具体的AOP代理对象的创建,AdvisedSupport把它交给它的子类们去完成

ProxyCreatorSupport,可以将它看成是其子类创建AOP代理对象的一个辅助类。

在通过继承基类的功能实现,具体的AOP代理对象的生成,根据不同的需要,分别由ProxyFactoryBean、AspectJProxyFactory后台ProxyFactory来完成。

提示:如果应用需要使用AspectJ AOP,AspectJProxyFactory起到集成Spring和AspectJ的作用。

提示:对于使用Spring AOP的应用,ProxyFactoryBean(可在IoC容器中完成声明式配置)和ProxyFactory(需要编程式使用Spring AOP)都提供了AOP功能的封装。

3.3.2 配置ProxyFactoryBean

在基于XML配置Spring的Bean时,往往需要一系列的配置步骤来使用ProxyFactoryBean和AOP。

1. 定义使用的通知器Advisor,该通知器应该作为一个Bean来定义。该通知器的实现定义了需要对目标对象进行增强的切面行为,即Advice通知。

2. (配置AOP,封装AOP功能的主要类)定义ProxyFactoryBean,将它作为另一个Bean来定义,它是封装AOP功能的主要类。

    2.1 AOP代理接口

    2.2 AOP切面增强的对象

    2.3 代理拦截器,配置通知器的名称(通知器在AOP代理的配置下通过使用代理对象的拦截器机制发挥作用)

3. 定义target属性,作为target属性注入的Bean,在需要AOP通知器中的切面应用来增强的对象。
1. 配置 ProxyFactoryBean 分析Spring AOP实现原理

ProxyFactoryBean是在 Spring IoC 环境中创建AOP应用的底层方法,是一个非常灵活的创建AOP应用的底层方法,Spring通过其完成对AOP使用的封装。

配置ProxyFactoryBean

3.3.3 ProxyFactoryBean生成AopProxy代理对象

我们知道通过 ProxyFactoryBean 来配置目标对象和切面行为。该 ProxyFactoryBean 是一个 FactoryBean。在ProxyFactoryBean中通过interceptorNames属性(通知器将切面行为Advice应用到目标对象中)来配置已经定义好的通知器 Advisor 。

在 ProxyFactoryBean 中需要为 target目标对象 生成 Proxy 代理对象,从而为AOP横切面的编织做好准备工作。

initializeAdvisorChain():初始化通知器链,通知器链中封装了一系列从配置中读取的拦截器,为代理对象的生成做好准备。 

getSingletonInstance():生成Singleton类型的Proxy 。

DefaultAopProxyFactory :AopProxy接口类,AopProxy有两个子类实现,一个是JdkDynamicAopProxy,一个是CglibProxyFactory 。

从FactoryBean中获取对象是以getObject方法作为入口完成的,ProxyFactoryBean实现中的getObject()方法,是FactoryBean需要实现的接口。对ProxyFactoryBean来说,需要对目标对象增加的增强处理,都通过了getObject方法进行了封装,这些增强处理是为AOP功能的实现提供服务的。getObject的实现清单如下。getObect()方法首先对通知器进行了初始化,通知器封装了一系列的拦截器,这些拦截器都要从配置文件中获取,然后为代理对象的生成做好准备。在生成代理对象时,因为Spring中有singleton类型和prototype类型这两种不同的bean,所有要对代理对象进行一个区分。

为Proxy代理对象配置Advisor链是在initializeAdvisorChain方法中实现的。这个初始化的工作发生在应用第一次通过ProxyFactoryBean去获取代理对象的时候。在完成这个初始化之后,接着读取配置中出现的所有通知器,这个取得通知器的过程也比较简单,把通知器的名字交给容器的getBean方法就可以了,这是通过对IOC容器实现的一个回调完成的。然后把从IOC容器中取得的通知器加入到拦截器链中,这个动作是由addAdvisorOnChainCreation方法来实现的。

接着在getObject()方法中,将会执行getSingletonInstance()方法,该方法主要是生成代理对象并封装对target目标对象的调用(即加入拦截器)。具体的生成过程是,首先读取ProxyFactoryBean中的配置,为生成代理对象做好必要的准备,比如设置代理的方法接口调用等。Spring通过AopProxy类来具体生成代理对象。对于getSingletonInstance()方法中代理对象的生成过程。

Spring利用AopProxy接口类把AOP代理对象的实现与框架其他部分有效隔离开来。AopProxy接口有两个子类实现,一个Cglib2AopProxy,另一个是JdkDynamicProxy。 具体代理对象的生成是在ProxyFactoryBean的基类AdvisedSupport中实现,借助AopProxyFactory完成,这个对象要么从JDK中生成,要么借助CGLIB获得。

AOP Proxy的生成的两种方式(JDK或CGLIB提供的Proxy特性)。如果目标对象是接口类使用JDK来生成,否则Spring会使用CGLIB来生成目标的代理对象。

在AopProxy代理对象的生成过程中,首先要从AdviseSupport对象中取得配置的目标对象,AOP完成的是切面应用对目标应用对象的增强。如果这里没有配置目标对象会直接抛出异常。一般而言,默认方式是使用JDK来产生AopProxy代理对象,但如果配置的目标对象不是接口类的实现,会使用CGLIB来产生AopProxy代理对象;在使用CGLIB来产生AopProxy代理对象时,因为CGLIB是第三方类库,本身不在JDK基类库中,所有需要在classPath中正确配置,以便能够加载和利用。在Spring中,使用JDK和CGLIB来生成AopProxy代理对象的工作,是由JdkDynamicAopProxy和CglibProxyFactory来完成。

3.3.4 JDK生成AopProxy代理对象

newProxyInstance方法:需要指明3个参数(类装载器,代理接口,Proxy回调方法所在的对象,这个对象要实现InvocationHandler接口)。InvocationHandler接口:类装载器,代理接口,Proxy回调方法所在的对象,这个对象要实现InvocationHandler接口。反射类接口,定义了invoke方法,提供代理对象的回调入口。

3.3.5 CGLIB生成AopProxy代理对象

配置Enhancer对象,通过Enhancer对象的callback回调设置生成代理对象。其中通过设置DynamicAdvisedInterceptor拦截器来完成AOP功能的。

3.4 Spring AOP拦截器调用的实现

3.4.1 设计原理

在Spring AOP通过JDK的Proxy方式或CGLIB方式生成代理对象的时候,相关的拦截器已经 配置到 代理对象中去了,拦截器在代理对象中起作用是通过对这些方法的回调来完成的。

如果使用JDK的Proxy来生成代理对象,那么需要InvocationHandler来设置拦截器回调,而如果使用CGLIB来生成代理对象,通过DynamicAdvisedInterceptor来完成回调。

3.4.2 JdkDynamicAopProxy的invoke拦截

在 JdkDynamicAopProxy 生成代理对象时,它的AopProxy代理对象生成调用。

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

- this 表示InvocationHandler对象,InvocationHandler是JDK定义反射类的一个接口,这个接口定义了invoke方法,此方法为回调方法。

通过invoke()方法的具体实现,来完成对目标对象方法调用的拦截器或者功能增强工作。在这个方法中,包含一个完整的拦截器链对目标对象的拦截过程,比如获取拦截器链中的拦截器进行配置,逐个运行拦截器链里的拦截器增强,知道最后的目标对象方法的运行。

3.4.3 CglibAopProxy的intercept拦截器

3.3.4 目标方法的调用

如果没有拦截器会对目标对象方法直接调用。对于JDKDynamicAopProxy代理对象是通过AopUtils使用反射机制实现的。在这个调用方法中首先得到调用方法的反射对象,然后使用invoke启动对方法反射对象的调用。

使用Cglib2AopProxy的代理对象,其目标对象的调用是通过CGLIB的MethodProxy对象直接完成。

retVal=methodProxy.invoke(target,args);

3.4.5 AOP拦截器的调用

在AOP如何完成对目标的增强时,这些都是封装在AOP拦截器链中,由具体的拦截器完成。

无论是使用JDKDynamicAopProxy还是使用CglibAopProxy创建代理对象最终对AOP拦截链的调用都是在ReflectiveMethodInvocation中通过proceed方法实现的。

在proceed方法中逐个运行拦截器的拦截方法。在运行拦截器的拦截方法之前需要对代理方法完成一个匹配,通过这个匹配判断来决定拦截器是否满足切面增强的要求。

3.4.6 配置通知器

在AopProxy代理对象拦截回调过程之前,我们先回到 ReflectionMethodInvocation 类的 proceed() 方法,在这个方法里,可以看到得到了配置的 interceptorOrInterceptionAdvice 。

Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

interceptorOrInterceptionAdvice是获得的拦截器,它通过拦截器机制对目标对象进行增强。这个拦截器来自interceptorsAndDynamicMethodMatchers。

具体来说上面的interceptorOrInterceptionAdvice是interceptorsAndDynamicMathers持有的List中的一个元素。

3.4.7 Advice通知的实现

AopProxy代理对象生成时,其拦截器也一并生成。

在为AopProxy配置拦截器的实现中,有一个取得拦截器配置过程,这个过程由DefaultAvisorChainFactory实现的,而这个工厂类负责生成拦截器链,在它的getInterceptorsAndDynamicInterceptionAdvice方法中,有一个适配器的注册过程,通过配置Spring预先设计好的拦截器,Spring加入了它对Aop实现的处理。

为详细了解这个过程,先从DefaultAdvisorChainFactory的实现开始,通过以下代码可以看到,在DefaultAdvisorChainFactory实现中,首先构造了一个GlobalAdvisorAdapterRegistry单件,然后对配置的Advisor通知器进行逐个遍历,这些通知链都是配置在interceptorNames中的,从getInterceptorsAndDynamicInterceptionAdvice传递进来的advised参数对象中,可以方便的取得配置的通知器,有了这些通知器,接着就是一个由 GlobalAdvisorAdapterRegistry来完成的拦截器的适配和注册。

GlobalAdvisorAdapterRegistry 的 getInterceptors() 方法为AOP的实现做出了很大的贡献,这个方法封装着 advice 织入实现的入口,我们先从 GlobalAdvisorAdapterRegistry 的实现入手,它基本起一个适配器的作用,但同时也是单例模式。

我们知道在DefaultAdvisorAdapterRegistry中,设置了一系列的adapter适配器,这是这些适配器的实现,为Spring的advice提供了编织能力。

Adapter的具体作用:

1. 调用adapter的support方法,通过这个方法来判断取得的advice属于什么类型的advice通知,从而根据不同的advice类型来注册不同的AdviceInterceptor,也就是前面我们看到的拦截器。

2. 这些AdviceInterceptor都是Spring AOP框架设计好的,是为实现不同的advice功能提供服务的。有了这些AdviceInterceptor,可以方便的使用由Spring提供的各种不同的advice来设计AOP应用。也就是说,正是这些AdviceInterceptor最终实现了advice通知在AopProxy对象中的织入功能。

3. 在DefaultAdvisorRegistry的getInterceptors调用中,从MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdaper这几个通知适配器的名字上可以看出和Advice一一对应,在这里,他们作为适配器被加入到adapter的List中,他们都是实现AdvisorAdapter接口的同一层次的类,只是各自承担着不同的适配的任务,一对一的服务于不同的advice实现。

Spring AOP为了实现advice的织入,设计了特定拦截器对这些功能进行了封装。虽然应用不会直接用到这些拦截器,但却是advice发挥作用不可缺少的准备。

3.6 小结

在通过ProxyFactoryBean实现AOP整个过程中,它的实现首先需要对 目标对象以及拦截器 进行正确的配置,以便 AopProxy代理对象 正确产生。(这些配置可以通过配置 ProxyFactoryBean 的属性来完成或通过编程式地使用ProxyFactory来实现)。

提示:通过 ProxyFactory 类,我们可以控制哪些方面需要被织入到代理中去。

在生成AopProxy代理对象的时候,Spring AOP专门设计了AopProxy代理对象的生产工厂(AopProxyFactory)来生产AopProxy代理对象。默认使用的生产工厂(DefaultAopProxyFactory - 该生产工厂定义了AopProxy代理对象的生成策略(JDK或CGLIB))。而最终的AopProxy对象的产生则交给JdkDynamicAopProxy和Cglib2AopProxy这两个具体的工厂来完成。

提示:我们可以看到 ProxyFactory 内部将生成代理对象的过程转交给一个 DefaultAopProxyFactory 对象来完成。后者又根据程序中的设置将其转交给一个 Cglib2AopProxy 或者 JdkDynamicAopProxy 来完成。

在得到AopProxy代理对象后,在代理的接口方法被调用执行的时候,在AopProxy暴露代理的方法被调用的时候,前面定义的Proxy机制就起作用。Proxy对象暴露的方法调用时,并不是直接运行目标对象中的方法。而是首先会触发对这些方法调用进行拦截,这些拦截对目标调用的增强提供了工作空间。

拦截过程在JDK的Proxy代理对象中,通过invoke()方法来完成,该方法是虚拟机触发的一个回调。在CGLIB的Proxy代理对象中,拦截是由设置好的回调callback()方法来完成的。

提示:在有了上面的拦截器的作用,才会有AOP切面的大作用。

在ProxyFactoryBean的回调中,首先会根据配置来对拦截器是否与当前的调用方法相匹配进行判断。

声明式的Spring AOP编程

首先看一下声明式的 Spring AOP 编程,在 ProxyFactoryBean 中,它的 AOP 实现需要依赖 JDK 或者 CGLIB 提供的 Proxy 特性。从 FactoryBean 中取得对象,是用 getObject() 方法作为入口完成的。

在 getObject() 方法中, Spring 使用这个 AopProxy 接口把 AOP 代理对象的实现与框架的其他部分有效地分离。 AopProxy 接口有两个子类实现,一个时 cglib2AopProxy ,另一个是 JdkDynamicProxy 。

创建 AopProxy 的过程是在 AopProxyFactory 中进行的,在 Spring 中, AopProxyFactory 使用的是 DefaultAopProxyFactory 。这个被使用的 DefaultAopProxyFactory ,作为 AopProxy 的创建工厂对象。有了这个 AopProxyFactory 对象以后,我们再到 DefaultAopProxyFactory 中看一下 AopProxy 是怎样生成的。

关于 AopProxy 代理对象的生产,需要考虑使用哪种生成方法,如果目标对象是接口类,那么适合使用 JDK 来生成代理对象,否则 Spring 会使用 CGLIB 来生成目标对象的代理对象。为了满足不同的代理对象生成的要求, DefaultAopProxyFactory 作为 AopProxy 的生产工厂,可以根据不同的需要生成两种 AopProxy 对象。

这样就分别得到了 JdkDynamicAopProxy 对象和 Cglib2AopProxy 对象,剩下的问题就是分别在两个产生代理对象的类中生成除代理对象。

本文标题:SPRING AOP

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:18:08

原始链接:http://bangjinhu.github.io/undefined/第3章 Spring AOP的实现/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.